<?php
namespace Illuminate\Foundation\Http;

use Illuminate\Contracts\Http\Kernel as KernelContract;
use Exception;
use Throwable;
use Illuminate\Routing\Router;
use Illuminate\Routing\Pipeline;
use Illuminate\Support\Facades\Facade;
use Illuminate\Contracts\Debug\ExceptionHandler;
use Illuminate\Contracts\Foundation\Application;
use Symfony\Component\Debug\Exception\FatalThrowableError;

class Kernel implements KernelContract {
    protected $app;
    protected $router;
    protected $bootstrappers = [
        \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
        \Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
        \Illuminate\Foundation\Bootstrap\HandleExceptions::class,
        \Illuminate\Foundation\Bootstrap\RegisterFacades::class,
        \Illuminate\Foundation\Bootstrap\RegisterProviders::class,
        \Illuminate\Foundation\Bootstrap\BootProviders::class,
    ];
    protected $middleware = [];
    protected $middlewareGroups = [];
    protected $routeMiddleware = [];
    protected $middlewarePriority = [
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \Illuminate\Auth\Middleware\Authenticate::class,
        \Illuminate\Session\Middleware\AuthenticateSession::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
        \Illuminate\Auth\Middleware\Authorize::class,
    ];

    public function __construct(Application $app, Router $router) {
        $this->app = $app;
        $this->router = $router;

        $router->middlewarePriority = $this->middlewarePriority;

        foreach ( $this->middlewareGroups as $key => $middleware ) {
            $router->middlewareGroup($key, $middleware);
        }

        foreach ( $this->routeMiddleware as $key => $middleware ) {
            $router->aliasMiddleware($key, $middleware);
        }
    }
    public function handle($request) { // 处理
        try {
            $request->enableHttpMethodParameterOverride(); // 启用 Putch 等

            $response = $this->sendRequestThroughRouter($request);
        } catch (Exception $e) {
            $this->reportException($e);

            $response = $this->renderException($request, $e);
        } catch (Throwable $e) {
            $this->reportException($e = new FatalThrowableError($e));

            $response = $this->renderException($request, $e);
        }

        $this->app['events']->dispatch(
            new Events\RequestHandled($request, $response)
        );

        return $response;
    }
    protected function sendRequestThroughRouter($request) { // 发送请求到对应路由
        $this->app->instance('request', $request);

        Facade::clearResolvedInstance('request');

        $this->bootstrap(); // 启动

        return (new Pipeline($this->app)) // 新建管道
            ->send($request) // 处理请求
            ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) // 中间件
            ->then($this->dispatchToRouter()); // 分发到路由
    }
    public function bootstrap() { // 启动
        if ( ! $this->app->hasBeenBootstrapped() ) $this->app->bootstrapWith($this->bootstrappers());
    }
    protected function dispatchToRouter() { // 分发到路由
        return function ($request) {
            $this->app->instance('request', $request);

            return $this->router->dispatch($request);
        };
    }

    public function terminate($request, $response) { // 终止
        $this->terminateMiddleware($request, $response); // 终止中间件

        $this->app->terminate(); // 终止主方法
    }
    protected function terminateMiddleware($request, $response) { // 终止中间件
        $middlewares = $this->app->shouldSkipMiddleware() ? [] : array_merge(
            $this->gatherRouteMiddleware($request),
            $this->middleware
        );

        foreach ( $middlewares as $middleware ) {
            if ( ! is_string($middleware) ) continue; // 只有 string 执行以下代码

            list($name) = $this->parseMiddleware($middleware); // 解析
            $instance = $this->app->make($name);
            if ( method_exists($instance, 'terminate') ) $instance->terminate($request, $response);
        }
    }
    protected function gatherRouteMiddleware($request) { // 收集路由中间件
        if ( $route = $request->route() ) return $this->router->gatherRouteMiddleware($route);

        return [];
    }
    protected function parseMiddleware($middleware) { // 解析中间件字符串
        list($name, $parameters) = array_pad(explode(':', $middleware, 2), 2, []);

        if ( is_string($parameters) ) $parameters = explode(',', $parameters);

        return [$name, $parameters];
    }
    public function hasMiddleware($middleware) { // 是否有指定中间件
        return in_array($middleware, $this->middleware);
    }
    public function prependMiddleware($middleware) { // 头部添加 中间件
        if ( array_search($middleware, $this->middleware) === false ) {
            array_unshift($this->middleware, $middleware);
        }

        return $this;
    }
    public function pushMiddleware($middleware) {
        if ( array_search($middleware, $this->middleware) === false ) { // 追加 中间件
            $this->middleware[] = $middleware;
        }

        return $this;
    }
    protected function bootstrappers() {
        return $this->bootstrappers;
    }
    protected function reportException(Exception $e) {
        $this->app[ExceptionHandler::class]->report($e);
    }
    protected function renderException($request, Exception $e) {
        return $this->app[ExceptionHandler::class]->render($request, $e);
    }
    public function getApplication() {
        return $this->app;
    }
}
